﻿using System.Collections.Generic;
using System.Threading.Tasks;

namespace Sidvall.Tasks
{
    public sealed class TaskCollection : System.Collections.ObjectModel.Collection<Task>
    {
        #region Public Members

        public event System.EventHandler<TaskCollectionEventArgs> ExecuteCompleted;

        #region AddRange

        public void AddRange(IEnumerable<Task> items)
        {
            if (items == null)
                return;
            foreach (var item in items)
            {
                if (item != null)
                    Add(item);
            }
        }

        #endregion
        #region AddAssignableTask

        public void AddAssignableTask<T>(Task<T> getValueTask, object target, string targetPropertyName, bool refresh)
        {
            if (getValueTask == null)
                return;
            if (target == null)
                return;
            var codeManager = Sidvall.SystemContext.Current.CodeManager;
            if (codeManager == null)
                codeManager = new Sidvall.Reflection.CodeManager(null);
            if (!refresh)
            {
                var value = codeManager.GetPropertyValue(target, targetPropertyName);
                if (value == null)
                    refresh = true;
            }
            if (refresh)
                Add(AssignableTask(codeManager, getValueTask, target, targetPropertyName));
        }

        #endregion
        #region ExecuteAsync

        public async Task ExecuteAsync()
        {
            var iMax = this.Count;
            for (int i = 0; i < iMax; i++)
            {
                var item = this[i];
                await item.ConfigureAwait(false);
                OnExecuteCompleted(i);
            }
        }

        #endregion
        #region ExecuteAsBatchesAsync

        public async Task ExecuteAsBatchesAsync(int batchSize)
        {
            var tasks = new List<Task>();
            var iMax = this.Count;
            for (int i = 0; i < iMax; i++)
            {
                tasks.Add(this[i]);
                if (tasks.Count >= batchSize)
                {
                    await Task.WhenAll(tasks).ConfigureAwait(false);
                    tasks.Clear();
                    OnExecuteCompleted(i);
                }
            }
            if (tasks.Count > 0)
                await Task.WhenAll(tasks).ConfigureAwait(false);
        }

        #endregion
        #region ExecuteAsOneAsync

        public async Task ExecuteAsOneAsync()
        {
            await Task.WhenAll(this).ConfigureAwait(false);
        }

        #endregion

        #region OnExecuteCompleted

        private void OnExecuteCompleted(long currentIndex)
        {
            OnExecuteCompleted(new TaskCollectionEventArgs(currentIndex));
        }
        private void OnExecuteCompleted(TaskCollectionEventArgs args)
        {
            if (this.ExecuteCompleted != null)
                this.ExecuteCompleted.Invoke(this, args);
        }

        #endregion

        #endregion
        #region Private Members

        #region AssignableTask

        private async Task AssignableTask<T>(Sidvall.Reflection.CodeManager codeManager, Task<T> getValueTask, object target, string targetPropertyName)
        {
            var value = await getValueTask.ConfigureAwait(false);
            codeManager.SetPropertyValue(target, value, targetPropertyName);
        }

        #endregion

        #endregion
    }
}
